/*
* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* openUBMC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*         http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/

#include <devmon/driver_abi.h>
#include <dlfcn.h>
#include <gtest/gtest.h>
#include <mc/dict.h>
#include <mc/engine/service.h>
#include <test_utilities/test_base.h>

#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

namespace drivers {
namespace pcie_nic_card {
namespace mellanox {
namespace cx5 {
namespace test {

class cx5_test_service : public mc::engine::service {
public:
    explicit cx5_test_service(const std::string& name) : mc::engine::service(name) {
    }
};

class cx5_abi_test : public mc::test::TestWithEngine {
protected:
    static void SetUpTestSuite() {
        TestWithEngine::SetUpTestSuite();
        m_test_service = std::make_unique<cx5_test_service>("bmc.openubmc.test_cx5");
        m_test_service->init();
        m_test_service->start();
        load_driver();
    }

    static void TearDownTestSuite() {
        if (m_test_service != nullptr) {
            m_test_service->stop();
            m_test_service.reset();
        }
        
        // 清理设备容器，避免引用已关闭的动态库
        m_devices.clear();
        
        // 不手动dlclose，避免静态对象析构时访问已卸载的动态库导致SIGSEGV
        // 让系统在程序退出时自动卸载动态库
        m_driver_handle = nullptr;
        
        TestWithEngine::TearDownTestSuite();
    }

    static void load_driver() {
        // 通过dlopen装载驱动，确保ABI接口可用
        m_driver_handle = dlopen("./opt/bmc/drivers/libcx5.so", RTLD_LAZY);
        ASSERT_NE(m_driver_handle, nullptr) << "dlopen failed: " << dlerror();

        auto func = reinterpret_cast<register_device_driver_func>(
            dlsym(m_driver_handle, "register_device_driver"));
        ASSERT_TRUE(func != nullptr) << "dlsym failed: " << dlerror();

        device_driver_t* devices = nullptr;
        uint8_t          count   = 0;
        status_t         ret     = func(&devices, &count);
        ASSERT_EQ(ret, STATUS_OK);

        m_devices.clear();
        for (uint8_t i = 0; i < count; ++i) {
            auto driver = std::make_shared<device_driver_t>(devices[i]);
            m_devices.emplace(driver->device_name, driver);
        }
    }

    static mc::dict create_test_connector() {
        return mc::dict{{"SystemId", 1}};
    }

    static std::unique_ptr<cx5_test_service>                                      m_test_service;
    static std::unordered_map<std::string, std::shared_ptr<device_driver_t>> m_devices;
    static void*                                                                  m_driver_handle;
};

std::unique_ptr<cx5_test_service>                                      cx5_abi_test::m_test_service;
std::unordered_map<std::string, std::shared_ptr<device_driver_t>> cx5_abi_test::m_devices;
void*                                                                  cx5_abi_test::m_driver_handle = nullptr;

// ========================= 驱动注册测试 =========================

TEST_F(cx5_abi_test, register_device_driver_success) {
    EXPECT_EQ(m_devices.size(), 3);
    EXPECT_TRUE(m_devices.find("PCIeNicCard") != m_devices.end());
    EXPECT_TRUE(m_devices.find("NicPort") != m_devices.end());
    EXPECT_TRUE(m_devices.find("OpticalTransceiver") != m_devices.end());
}

TEST_F(cx5_abi_test, device_driver_structure_validation) {
    for (const auto& entry : m_devices) {
        const auto& driver = entry.second;
        EXPECT_FALSE(std::string(driver->device_name).empty());
        EXPECT_NE(driver->ctor, nullptr);
        EXPECT_NE(driver->init, nullptr);
        EXPECT_NE(driver->start, nullptr);
        EXPECT_NE(driver->stop, nullptr);
    }
}

// ========================= PCIeNicCard设备测试 =========================

TEST_F(cx5_abi_test, create_cx5_card_success) {
    auto device_driver = m_devices["PCIeNicCard"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(m_test_service.get(), "cx5_card");
    EXPECT_NE(device, nullptr);
}

TEST_F(cx5_abi_test, create_cx5_card_null_service) {
    auto device_driver = m_devices["PCIeNicCard"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(nullptr, "cx5_card");
    EXPECT_EQ(device, nullptr);
}

TEST_F(cx5_abi_test, init_cx5_card_null_params) {
    auto device_driver = m_devices["PCIeNicCard"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(m_test_service.get(), "cx5_card_null");
    ASSERT_NE(device, nullptr);
    status_t ret = device_driver->init(device, nullptr, nullptr);
    EXPECT_EQ(ret, STATUS_ERROR);
}

// ========================= NicPort设备测试 =========================

TEST_F(cx5_abi_test, create_cx5_port_success) {
    auto device_driver = m_devices["NicPort"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(m_test_service.get(), "cx5_port");
    EXPECT_NE(device, nullptr);
}

TEST_F(cx5_abi_test, create_cx5_port_null_service) {
    auto device_driver = m_devices["NicPort"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(nullptr, "cx5_port");
    EXPECT_EQ(device, nullptr);
}

TEST_F(cx5_abi_test, init_cx5_port_null_params) {
    auto device_driver = m_devices["NicPort"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(m_test_service.get(), "cx5_port_null");
    ASSERT_NE(device, nullptr);
    status_t ret = device_driver->init(device, nullptr, nullptr);
    EXPECT_EQ(ret, STATUS_ERROR);
}

// ========================= OpticalTransceiver设备测试 =========================

TEST_F(cx5_abi_test, create_cx5_optical_module_success) {
    auto device_driver = m_devices["OpticalTransceiver"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(m_test_service.get(), "cx5_om");
    EXPECT_NE(device, nullptr);
}

TEST_F(cx5_abi_test, create_cx5_optical_module_null_service) {
    auto device_driver = m_devices["OpticalTransceiver"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(nullptr, "cx5_om");
    EXPECT_EQ(device, nullptr);
}

TEST_F(cx5_abi_test, init_cx5_optical_module_null_params) {
    auto device_driver = m_devices["OpticalTransceiver"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(m_test_service.get(), "cx5_om_null");
    ASSERT_NE(device, nullptr);
    status_t ret = device_driver->init(device, nullptr, nullptr);
    EXPECT_EQ(ret, STATUS_ERROR);
}

// ========================= 空指针错误处理 =========================

TEST_F(cx5_abi_test, device_pcienicard_with_null_device) {
    auto device_driver = m_devices["PCIeNicCard"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(m_test_service.get(), nullptr);
    EXPECT_EQ(device, nullptr);
    device = device_driver->ctor(nullptr, "cx5_card_null_params");
    EXPECT_EQ(device, nullptr);
    mc::mutable_dict csr;
    auto             connector = create_test_connector();
    status_t         init_ret  = device_driver->init(nullptr, &csr, &connector);
    EXPECT_EQ(init_ret, STATUS_ERROR);
    EXPECT_EQ(device_driver->start(nullptr), STATUS_ERROR);
    EXPECT_EQ(device_driver->stop(nullptr), STATUS_ERROR);
}

TEST_F(cx5_abi_test, device_nicport_with_null_params) {
    auto device_driver = m_devices["NicPort"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(m_test_service.get(), nullptr);
    EXPECT_EQ(device, nullptr);
    device = device_driver->ctor(nullptr, "cx5_port_null_params");
    EXPECT_EQ(device, nullptr);
    mc::mutable_dict csr;
    auto             connector = create_test_connector();
    status_t         init_ret  = device_driver->init(nullptr, &csr, &connector);
    EXPECT_EQ(init_ret, STATUS_ERROR);
    EXPECT_EQ(device_driver->start(nullptr), STATUS_ERROR);
    EXPECT_EQ(device_driver->stop(nullptr), STATUS_ERROR);
}

TEST_F(cx5_abi_test, device_opticalmodule_with_null_params) {
    auto device_driver = m_devices["OpticalTransceiver"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(m_test_service.get(), nullptr);
    EXPECT_EQ(device, nullptr);
    device = device_driver->ctor(nullptr, "cx5_om_null_params");
    EXPECT_EQ(device, nullptr);
    mc::mutable_dict csr;
    auto             connector = create_test_connector();
    status_t         init_ret  = device_driver->init(nullptr, &csr, &connector);
    EXPECT_EQ(init_ret, STATUS_ERROR);
    EXPECT_EQ(device_driver->start(nullptr), STATUS_ERROR);
    EXPECT_EQ(device_driver->stop(nullptr), STATUS_ERROR);
}

// ========================= 启停与多实例测试 =========================

TEST_F(cx5_abi_test, start_stop_cx5_card_success) {
    auto device_driver = m_devices["PCIeNicCard"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(m_test_service.get(), "cx5_card_start_stop");
    ASSERT_NE(device, nullptr);
    mc::mutable_dict csr;
    auto             connector = create_test_connector();
    device_driver->init(device, &csr, &connector);
    EXPECT_EQ(device_driver->start(device), STATUS_OK);
    EXPECT_EQ(device_driver->stop(device), STATUS_OK);
}

TEST_F(cx5_abi_test, start_stop_cx5_port_success) {
    auto device_driver = m_devices["NicPort"];
    ASSERT_NE(device_driver, nullptr);
    void* device = device_driver->ctor(m_test_service.get(), "cx5_port_start_stop");
    ASSERT_NE(device, nullptr);
    mc::mutable_dict csr;
    auto             connector = create_test_connector();
    device_driver->init(device, &csr, &connector);
    EXPECT_EQ(device_driver->start(device), STATUS_OK);
    EXPECT_EQ(device_driver->stop(device), STATUS_OK);
}

TEST_F(cx5_abi_test, multiple_device_instances) {
    auto card_driver = m_devices["PCIeNicCard"];
    auto port_driver = m_devices["NicPort"];
    auto om_driver   = m_devices["OpticalTransceiver"];
    ASSERT_NE(card_driver, nullptr);
    ASSERT_NE(port_driver, nullptr);
    ASSERT_NE(om_driver, nullptr);

    std::vector<void*> card_devices;
    std::vector<void*> port_devices;
    std::vector<void*> om_devices;

    for (int i = 0; i < 3; ++i) {
        std::string name = "cx5_card_" + std::to_string(i);
        card_devices.push_back(card_driver->ctor(m_test_service.get(), name.c_str()));
    }

    for (int i = 0; i < 3; ++i) {
        std::string name = "cx5_port_" + std::to_string(i);
        port_devices.push_back(port_driver->ctor(m_test_service.get(), name.c_str()));
    }

    for (int i = 0; i < 2; ++i) {
        std::string name = "cx5_om_" + std::to_string(i);
        om_devices.push_back(om_driver->ctor(m_test_service.get(), name.c_str()));
    }

    EXPECT_EQ(card_devices.size(), 3);
    EXPECT_EQ(port_devices.size(), 3);
    EXPECT_EQ(om_devices.size(), 2);
}

} // namespace test
} // namespace cx5
} // namespace mellanox
} // namespace pcie_nic_card
} // namespace drivers
